#include "Shell.h"
#include "Cmds.h"

#if CONFIG_SHELL
#define PROMPT "\rsh >"
typedef int (*cmd_function_t)(int argc, char **argv);

static char line[SHELL_CMD_SIZE];
static U8 line_position;

struct shell_syscall *_syscall_table_begin  = NULL;
struct shell_syscall *_syscall_table_end    = NULL;

/*******************************************************************************
  * 函数名：Shell_SplitArgs
  * 功  能：Shell分割命令参数
  * 参  数：char *cmd：命令
			size_t length：命令长度
			char *argv[SHELL_ARG_NR]：参数列表
  * 返回值：int argc：命令行参数数量，包括命令行本身
  * 说  明：无
*******************************************************************************/
static int Shell_SplitArgs(char *cmd, size_t length, char *argv[SHELL_ARG_NR])
{
    char *ptr;
    size_t position, argc;

    ptr = cmd;
    position = 0;
    argc = 0;

    while (position < length)
    {
        /* strip bank and tab */
        while ((*ptr == ' ' || *ptr == '\t') && position < length)
        {
            *ptr = '\0';
            ptr ++;
            position ++;
        }
        if (position >= length) break;

        /* handle string */
        if (*ptr == '"')
        {
            ptr ++;
            position ++;
            argv[argc] = ptr;
            argc ++;

            /* skip this string */
            while (*ptr != '"' && position < length)
            {
                if (*ptr == '\\')
                {
                    if (*(ptr + 1) == '"')
                    {
                        ptr ++;
                        position ++;
                    }
                }
                ptr ++;
                position ++;
            }
            if (position >= length) break;

            /* skip '"' */
            *ptr = '\0';
            ptr ++;
            position ++;
        }
        else
        {
            argv[argc] = ptr;
            argc ++;
            while ((*ptr != ' ' && *ptr != '\t') && position < length)
            {
                ptr ++;
                position ++;
            }
            if (position >= length) break;
        }
    }

    return argc;
}

/*******************************************************************************
  * 函数名：Shell_GetCmdFun
  * 功  能：Shell获取命令对应Function
  * 参  数：char *cmd：命令
			size_t size：命令长度
  * 返回值：cmd_function_t:对应CMD的执行函数
  * 说  明：无
*******************************************************************************/
static cmd_function_t Shell_GetCmdFun(char *cmd, int size)
{
    struct shell_syscall *index = NULL;

    for (index = _syscall_table_begin; index < _syscall_table_end; index++)
    {
        if (strncmp(index->name, cmd, size) == 0 && index->name[size] == '\0')
            return (cmd_function_t)index->func;
    }

    return NULL;
}

/*******************************************************************************
  * 函数名：Shell_ExecuteCmd
  * 功  能：Shell命令执行
  * 参  数：char *cmd：命令
			size_t length：命令长度
			int *retp：执行函数返回值
  * 返回值： 0：命令正常执行  
			<0: 命令执行异常
  * 说  明：无
*******************************************************************************/
static int Shell_ExecuteCmd(char *cmd, size_t length, int *retp)
{
    int argc;
    int cmd0_size = 0;
    cmd_function_t cmd_func = NULL;
    char *argv[SHELL_ARG_NR];

    /* find the size of first command */
    while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
        cmd0_size ++;

    if (cmd0_size == 0) 
	{
        DEBUG("cmd size is 0\r\n");
		return -5;//return -EINVAL;
	}

    cmd_func = Shell_GetCmdFun(cmd, cmd0_size);
    if (!cmd_func) 
	{
        DEBUG("cmd :%s func is null\r\n",cmd);
		return -5;// return -EINVAL;
	}

    /* split arguments */
    memset(argv, 0x00, sizeof(argv));
    argc = Shell_SplitArgs(cmd, length, argv);
    if (argc == 0) 
	{
        DEBUG("cmd argc is 0\r\n");
		return -5;// return -EINVAL;
	}

    /* exec this command */
    *retp = cmd_func(argc, argv);
    return 0;
}

/*******************************************************************************
  * 函数名：Shell_Execute
  * 功  能：Shell命令解析、执行
  * 参  数：char *cmd：命令
			size_t length：命令长度
  * 返回值： 1: 命令行函数成功运行
			 0：命令长度错误
			-1：命令参数错误
			-2：命令没有找到
			其他：
  * 说  明：无
*******************************************************************************/
static int Shell_Execute(char *cmd, size_t length)
{
    int cmd_ret;

    /* strim the beginning of command */
    while (*cmd  == ' ' || *cmd == '\t')
    {
        cmd++;
        length--;
    }

    if (length == 0) return SHELL_RET_CMD_LENERR;

	// 命令执行
    if (Shell_ExecuteCmd(cmd, length, &cmd_ret) == 0)
	{
		if(cmd_ret == SHELL_RET_CMD_ARGERR)
		{
			// 命令参数错误
            DEBUG("%s para error\r\n",cmd);
		}
		return cmd_ret;
	}
	
    /* truncate the cmd at the first space. */
    {
        char *tcmd = cmd;
        while (*tcmd != ' ' && *tcmd != '\0')
            tcmd++;
        *tcmd = '\0';
    }
    DEBUG("%s: command not found.\r\n", cmd);
    return SHELL_RET_CMD_NOTFUOND;
}

static int str_common(const char *str1, const char *str2)
{
    const char *str = str1;

    while ((*str != 0) && (*str2 != 0) && (*str == *str2))
    {
        str ++;
        str2 ++;
    }

    return (str - str1);
}

/*******************************************************************************
  * 函数名：Shell_AutoComplete
  * 功  能：Shell自动补全
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
static void Shell_AutoComplete(char *prefix)
{
	int length, min_length = 0;
    const char *name_ptr = NULL, *cmd_name = NULL;
    struct shell_syscall *index = NULL;
	
    DEBUG("\r\n");
    if (*prefix == '\0')
    {
        Msh_Help(0, NULL);
        return;
    }

    for (index = _syscall_table_begin; index < _syscall_table_end; index++)
    {
        cmd_name = (const char *)index->name;
        if (!strncmp(prefix, cmd_name, strlen(prefix)))
        {
            if (min_length == 0)
            {
                name_ptr = cmd_name;
                min_length = strlen(name_ptr);
            }

            length = str_common(name_ptr, cmd_name);
            if (length < min_length)
                min_length = length;

            DEBUG("%s\r\n", cmd_name);
        }
    }

    if (name_ptr) strncpy(prefix, name_ptr, min_length);
    DEBUG("%s%s", PROMPT, prefix);
}

/*******************************************************************************
  * 函数名：Shell_Handle
  * 功  能：Shell处理
  * 参  数：无
  * 返回值：无
  * 说  明：接收到回车换行时，进入Shell_Handle
*******************************************************************************/
static void Shell_Handle(void)
{
    Shell_Execute(line, line_position);
    DEBUG(PROMPT);
    DEBUG("\r");
    memset(line, 0, sizeof(line));
    line_position = 0;
}

/*******************************************************************************
  * 函数名：Shell_RecvChr
  * 功  能：Shell接收字符
  * 参  数：char ch:Shell端口接收到的字符
  * 返回值：无
  * 说  明：接收字符时调用
*******************************************************************************/
void Shell_RecvChr(char ch)
{
    switch (ch)
    {
	// 自动补全
    case '\t':
    {
        int i;

        for (i = 0; i < line_position; i++)
            DEBUG("\b");

        Shell_AutoComplete(&line[0]);
        line_position = strlen(line);

        break;
    }
    case 0x08:  /* Backspace */
    case 0x7F:   /* Delete */
    {
        if (line_position == 0)
            break;

        line_position--;

        DEBUG("\b \b");
        line[line_position] = 0;
        break;
    }
	// 回车换行，命令输入完成，开始解析
    case '\n':
    case '\r':
    {
        DEBUG("\r\n");
        Shell_Handle();
        break;
    }
    default:
    {
        /* it's a large line, discard it */
        if (line_position >= SHELL_CMD_SIZE)
            line_position = 0;

        /* normal character */
        line[line_position] = ch;
        DEBUG("%c", ch);

        ch = 0;
        line_position++;
        if (line_position >= SHELL_CMD_SIZE)
            line_position = 0;
        break;
    }
    } /* switch(ch) */
}

/*******************************************************************************
  * 函数名：Shell_Init
  * 功  能：初始化Shell
  * 参  数：无
  * 返回值：无
  * 说  明：无
*******************************************************************************/
void Shell_Init(void)
{
	_syscall_table_begin = (struct shell_syscall *)CMDTab_GetTabStartAddr();
	_syscall_table_end   = (struct shell_syscall *)CMDTab_GetTabEndAddr();
	
    DEBUG(PROMPT);
}

#endif

